﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows;
using System.Diagnostics;

namespace NewSangoBang
{
    /// <summary>
    /// Handle the game. This class is designed to code and decode xml messages. It does nothing about threads, so ones who bind delegates to the events should settle threads.
    /// </summary>
    class GameHandler : System.Windows.Threading.DispatcherObject
    {
        public event MessageEventHandler onJoinResultGiven;
        public event MessageEventHandler onUserLeaveResultGiven;
        public event MessageEventHandler onPlayerNameListRefreshed;
        public event MessageEventHandler onRoleCardGiven;
        public event MessageEventHandler onCharacterCardGiven;
        public event MessageEventHandler onGameCardDispatched;
        public event MessageEventHandler onDispatchCardFinish;
        public event MessageEventHandler onPlayerActionDo;

        private MessageController messageController;

        private GameHandler()
        {
            this.messageController = new MessageController(new NewMessage(this.OnReceiveMessage));
        }

        private static GameHandler instance = new GameHandler();
        public Module module = new Module();
        /// <summary>
        /// Get the instance of this singleton class.
        /// </summary>
        static public GameHandler GetInstance() { return instance; }

        private void OnReceiveMessage(string message)
        {
            string result = "";
            List<object> arg = new List<object>();//A list storing event arguments
            XDocument xdocument = XDocument.Parse(message);
            XElement root = xdocument.Element("message");
            Debug.Assert(root.Name == "message");
            Debug.WriteLine(root.Attribute("type").Value);

            switch (root.Attribute("type").Value)
            {
                case "joinResult":
                    arg.Clear();
                    module.Guid = root.Element("guid").Value;
                    module.IsHost = root.Attribute("ishost").Value;
                    arg.Add(root.Attribute("result").Value);
                    arg.Add("");//root.Element("detail").Value;
                    FireUIEvent(onJoinResultGiven, arg); 
                    break;

                case "startGameResult":
                    arg.Clear();
                    arg.Add(root.Attribute("result").Value);
                    break;

                case "userLeaveResult":
                    arg.Clear();
                    arg.Add(root.Attribute("result").Value);
                    FireUIEvent(onUserLeaveResultGiven, arg);
                    break;

                case "playerList":
                    arg.Clear();
                    List<string> playerNames = new List<string>(from i in root.Elements("player") select i.Value);
                    module.PlayerList = playerNames;
                    FireUIEvent(onPlayerNameListRefreshed, arg); 
                    break;

                case "roleCard":
                    arg.Clear();
                    module.Role = root.Element("role").Value;
                    FireUIEvent(onRoleCardGiven, arg);
                    break;

                case "characterCard":
                    arg.Clear();
                    var elementss = from i in root.Elements("character") select i;
                    List<Character> characters = new List<Character>();
                    foreach (XElement e in elementss)
                        characters.Add(new Character(e.Attribute("player").Value, e.Value, int.Parse(e.Attribute("hp").Value), e.Attribute("description").Value));
                    module.CharacterList = characters;
                    FireUIEvent(onCharacterCardGiven, arg);
                    break;

                case "gameCard":
                    arg.Clear();
                    List<string> gameCards = new List<string>(from i in root.Elements("gameCard") select i.Value);
                    module.GameCards = gameCards;
                    FireUIEvent(onGameCardDispatched, arg);
                    break;

                case "dispatchCardFinish":
                    arg.Clear();
                    FireUIEvent(onDispatchCardFinish, arg);
                    break;

                case "playerAction":
                    arg.Clear();
                    XElement action = root.Element("action");
                    arg.Add(action);
                    FireUIEvent(onPlayerActionDo, arg);
                    break;

                default:
                    break;
 
            }
        }

        private void FireUIEvent(MessageEventHandler handler, List<object> arg)
        {
            Delegate[] handlers = handler.GetInvocationList();
            foreach (var ui_handler in handlers)
            {
                System.Windows.Threading.DispatcherObject obj = (System.Windows.Threading.DispatcherObject)ui_handler.Target;
                obj.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, ui_handler, this, arg);
            }
        }
        /// <summary>
        /// ########## This is just a test method. Message coding and decoding should be done in GameHandler.
        /// </summary>
        /// <param name="message"></param>
        public void SendMessage(string message)
        {
            this.messageController.SendMessage(message);
        }

        /// <summary>
        /// Defines the returns of the joining.
        /// </summary>
        public enum RegisterReturns { Success, UsedPlayerName, UnKnownError };

        /// <summary>
        /// Join the game.
        /// </summary>
        /// <param name="playerName"></param>
        /// <returns></returns>
        public RegisterReturns Join(string playerName)
        {
            try
            {
                string r = messageController.Register(playerName);
                XDocument doc = XDocument.Parse(r);
                XElement root = doc.Root;
                if (root.Attribute("result").Value == "success")
                {
                    this.OnReceiveMessage(r);
                    return RegisterReturns.Success;
                }
                else
                    if (root.Element("detail").Value == "UsedPlayerName")
                        return RegisterReturns.UsedPlayerName;
                    else
                        return RegisterReturns.UnKnownError;
            }
            catch(Exception ex)
            {
                return RegisterReturns.UnKnownError;
            }
        }

        /// <summary>
        /// Leave the game.
        /// </summary>
        /// <returns>true, if leave success.</returns>
        public bool Leave()
        {
            try
            {
                string r = messageController.SendMessage("<message type=\"userLeave\"></message>");
                XDocument doc = XDocument.Parse(r);
                XElement root = doc.Root;
                if (root.Attribute("result").Value == "success")
                {
                    return true;
                }
                else
                    return false;
            }
            catch
            {
                return false;
            }

        }
    }
}
